package org.tamal.information;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Typeface;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.BatteryManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.text.SpannableString;
import android.text.style.StyleSpan;
import android.text.style.UnderlineSpan;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;

/**
 * The information activity.
 * @author Tamal Kanti Nath
 */
public final class Information extends Activity {

    private LinearLayout layout;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        layout = new LinearLayout(this);
        layout.setOrientation(LinearLayout.VERTICAL);
        addTable(R.string.information_sys_prop, System.getProperties());
        addTable(R.string.information_env_vars, System.getenv());
        addTable(R.string.information_build, getBuildProperties());
        addTable(R.string.information_battery, getBatteryProperties());
        addTable(R.string.information_environment, getEnvironment());
        addSensorProperties();
        addLocationProperties();
        addAccounts();
        addTable(R.string.information_other, getOtherProperties());
        ScrollView scroll = new ScrollView(this);
        scroll.addView(layout);
        setContentView(scroll);
    }

    @SuppressLint("NewApi")
    private static Map<String, Object> getBuildProperties() {
        Map<String, Object> map = new TreeMap<String, Object>();
        map.put("BOARD", Build.BOARD);
        map.put("BOOTLOADER", Build.BOOTLOADER);
        map.put("BRAND", Build.BRAND);
        map.put("CPU_ABI", Build.CPU_ABI);
        map.put("CPU_ABI2", Build.CPU_ABI2);
        map.put("DEVICE", Build.DEVICE);
        map.put("DISPLAY", Build.DISPLAY);
        map.put("FINGERPRINT", Build.FINGERPRINT);
        map.put("HARDWARE", Build.HARDWARE);
        map.put("HOST", Build.HOST);
        map.put("ID", Build.ID);
        map.put("MANUFACTURER", Build.MANUFACTURER);
        map.put("MODEL", Build.MODEL);
        map.put("PRODUCT", Build.PRODUCT);
        map.put("SERIAL", Build.SERIAL);
        map.put("TAGS", Build.TAGS);
        map.put("TIME", new Date(Build.TIME));
        map.put("TYPE", Build.TYPE);
        map.put("USER", Build.USER);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            map.put("RadioVersion", Build.getRadioVersion());
        }
        map.put("VERSION.CODENAME", Build.VERSION.CODENAME);
        map.put("VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL);
        map.put("VERSION.RELEASE", Build.VERSION.RELEASE);
        map.put("VERSION.SDK_INT", Build.VERSION.SDK_INT);
        return map;
    }

    private Map<String, Object> getOtherProperties() {
        Map<String, Object> map = new TreeMap<String, Object>();
        Display display = getWindowManager().getDefaultDisplay();
        DisplayMetrics matrics = new DisplayMetrics();
        display.getMetrics(matrics);
        map.put("density", matrics.density);
        map.put("scaled density", matrics.scaledDensity);
        map.put("density (DPI)", matrics.densityDpi  + " (" + matrics.xdpi
                + " x " + matrics.ydpi + ")");
        map.put("size (px)", matrics.widthPixels + "x" + matrics.heightPixels);
        map.put("refresh rate", display.getRefreshRate());
        map.put("rotation", display.getRotation());
        map.put("date", new Date());
        return map;
    }

    private Map<String, Object> getBatteryProperties() {
        Map<String, Object> map = new TreeMap<String, Object>();
        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        Intent battery = registerReceiver(null, filter);
        map.put("battery present",
                battery.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true));
        int status = battery.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
        String str;
        str = getName(BatteryManager.class, "BATTERY_STATUS_", status);
        map.put("battery status", str);
        status = battery.getIntExtra(BatteryManager.EXTRA_HEALTH, -1);
        str = getName(BatteryManager.class, "BATTERY_HEALTH_", status);
        map.put("battery health", str);
        str = battery.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);
        map.put("battery technology", str);
        int temp = battery.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1);
        map.put("battery temperature", (temp / 10.0f) + " c");
        int voltage = battery.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1);
        map.put("battery voltage", (voltage / 1000.0f) + "v");
        status = battery.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
        str = getName(BatteryManager.class, "BATTERY_PLUGGED_", status);
        map.put("battery charger", str);
        int level = battery.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        int scale = battery.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        map.put("battery charge", (level * 100 / scale) + "%");
        return map;
    }

    private void addAccounts() {
        addTitle(R.string.information_account);
        TableLayout table = new TableLayout(this);
        addHeaders(table, R.string.information_name, R.string.information_type);
        for (Account account : AccountManager.get(this).getAccounts()) {
            List<String> strings = new ArrayList<String>();
            strings.add(account.name);
            strings.add(account.type);
            addItems(table, strings);
        }
        HorizontalScrollView scroll = new HorizontalScrollView(this);
        scroll.addView(table);
        layout.addView(scroll);
    }

    private void addSensorProperties() {
        addTitle(R.string.information_sensor);
        TableLayout table = new TableLayout(this);
        addHeaders(table, "Name", "Type", "Vendor", "Version", "Min Delay",
                "Power", "Max Range", "Resolution");
        SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE);
        List<Sensor> sensors = sm.getSensorList(Sensor.TYPE_ALL);
        for (Sensor sensor : sensors) {
            List<String> strings = new ArrayList<String>(sensors.size() * 8);
            strings.add(sensor.getName());
            strings.add(getName(Sensor.class, "TYPE_", sensor.getType()));
            strings.add(sensor.getVendor());
            strings.add(String.valueOf(sensor.getVersion()));
            strings.add(String.valueOf(sensor.getMinDelay()));
            strings.add(String.valueOf(sensor.getPower()));
            strings.add(String.valueOf(sensor.getMaximumRange()));
            strings.add(String.valueOf(sensor.getResolution()));
            addItems(table, strings);
        }
        HorizontalScrollView scroll = new HorizontalScrollView(this);
        scroll.addView(table);
        layout.addView(scroll);
    }

    private void addLocationProperties() {
        addTitle(R.string.information_location);
        TableLayout table = new TableLayout(this);
        addHeaders(table, "Name", "Latitude", "Longitude", "Accuracy",
                "Altitude", "Bearing", "Speed", "Provider", "Time", "Accuracy",
                "Power", "Cell", "Network", "Satellite");
        LocationManager manager =
                (LocationManager) getSystemService(LOCATION_SERVICE);
        List<String> providers = manager.getAllProviders();
        for (String name : providers) {
            Location location = manager.getLastKnownLocation(name);
            LocationProvider locProv = manager.getProvider(name);
            List<String> strings = new ArrayList<String>(providers.size() * 14);
            strings.add(name);
            if (location == null) {
                strings.add(null);
                strings.add(null);
                strings.add(null);
                strings.add(null);
                strings.add(null);
                strings.add(null);
                strings.add(null);
                strings.add(null);
            } else {
                strings.add(String.valueOf(location.getLatitude()));
                strings.add(String.valueOf(location.getLongitude()));
                strings.add(String.valueOf(location.getAccuracy()));
                strings.add(String.valueOf(location.getAltitude()));
                strings.add(String.valueOf(location.getBearing()));
                strings.add(String.valueOf(location.getSpeed()));
                strings.add(String.valueOf(location.getProvider()));
                strings.add(String.valueOf(new Date(location.getTime())));
            }
            strings.add(String.valueOf(getName(Criteria.class, "ACCURACY_",
                    locProv.getAccuracy())));
            strings.add(String.valueOf(getName(Criteria.class, "POWER_",
                    locProv.getPowerRequirement())));
            strings.add(String.valueOf(locProv.requiresCell()));
            strings.add(String.valueOf(locProv.requiresNetwork()));
            strings.add(String.valueOf(locProv.requiresSatellite()));
            addItems(table, strings);
        }
        HorizontalScrollView scroll = new HorizontalScrollView(this);
        scroll.addView(table);
        layout.addView(scroll);
    }

    @SuppressLint("NewApi")
	private Map<String, Object> getEnvironment() {
        Map<String, Object> map = new TreeMap<String, Object>();
        map.put("Data Directory", Environment.getDataDirectory());
        map.put("Download Cache Directory",
                Environment.getDownloadCacheDirectory());
        map.put("External Storage Directory",
                Environment.getExternalStorageDirectory());
        map.put("External Storage State",
                Environment.getExternalStorageState());
        map.put("Root Directory",
                Environment.getRootDirectory());
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            map.put("External Storage Emulated",
                    Environment.isExternalStorageEmulated());
        }
        map.put("External Storage Removable",
                Environment.isExternalStorageRemovable());
        return map;
    }

    private void addTable(int title, Map<?, ?> map) {
        addTitle(title);
        TableLayout table = new TableLayout(this);
        addHeaders(table, R.string.information_key, R.string.information_value);
        TextView view;
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            TableRow row = new TableRow(this);
            view = new TextView(this);
            view.setText(String.valueOf(entry.getKey()));
            row.addView(view);
            view = new TextView(this);
            view.setText(String.valueOf(entry.getValue()));
            row.addView(view);
            table.addView(row);
        }
        HorizontalScrollView scroll = new HorizontalScrollView(this);
        scroll.addView(table);
        layout.addView(scroll);
    }

    private void addTitle(int title) {
        TextView view = new TextView(this);
        SpannableString span = new SpannableString(getString(title));
        span.setSpan(new UnderlineSpan(), 0, span.length(), 0);
        StyleSpan style = new StyleSpan(Typeface.BOLD);
        span.setSpan(style, 0, span.length(), 0);
        view.setText(span);
        view.setTextSize(20f);
        layout.addView(view);
    }

    private void addHeaders(ViewGroup viewGroup, String... ids) {
        TableRow row = new TableRow(this);
        for (String id : ids) {
            TextView view = new TextView(this);
            view.setText(id);
            view.setTextColor(0xffff0000);
            row.addView(view);
        }
        viewGroup.addView(row);
    }

    private void addHeaders(ViewGroup viewGroup, int... ids) {
        TableRow row = new TableRow(this);
        for (int id : ids) {
            TextView view = new TextView(this);
            view.setText(id);
            view.setTextColor(0xffff0000);
            row.addView(view);
        }
        viewGroup.addView(row);
    }

    private void addItems(ViewGroup viewGroup, List<String> items) {
        TableRow row = new TableRow(this);
        for (String item : items) {
            TextView view = new TextView(this);
            view.setText(item);
            row.addView(view);
        }
        viewGroup.addView(row);
    }

    private static String getName(Class<?> cls, String prefix, int value) {
        for (Field f: cls.getDeclaredFields()) {
            int m = f.getModifiers();
            try {
                if (Modifier.isStatic(m)
                    && Modifier.isFinal(m)
                    && int.class.equals(f.getType())
                    && f.getName().startsWith(prefix)
                    && f.getInt(null) == value) {
                    return f.getName().substring(prefix.length());
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }
}
